#include "ctl_srv.h"

static struct ev_timer* TIMEOUT_WTR;
static struct bcshm_t shm;

int init_ctl_srv(int host, int port){

  int srv = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(port);
  addr.sin_addr.s_addr = htonl(host);

  assert(bind(srv, (struct sockaddr*) &(addr), sizeof(addr)) != -1);
  listen(srv, 1);

  return srv;
}

void run_ctl_srv(){
  int srv = init_ctl_srv(CTL_SERVER_IP, CTL_SERVER_PORT);

  bcshm_open(&shm);

  ev_io sock_wtr;
  
  struct ev_loop* loop = ev_loop_new(EVFLAG_NOENV | EVBACKEND_EPOLL);
  ev_io_init(&sock_wtr, on_accept_cb, srv, EV_READ);
  ev_io_start(loop, &sock_wtr);

  //printf("Start ctl server...\n");
  ev_run(loop, 0);
}

void enable_keepalive(int sock){
  int yes = 1;
  assert(setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(int)) != -1);

  int idle = 1;
  assert(setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(int)) != -1);

  int interval = 5;
  assert(setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(int)) != -1);

  int maxpkt = 100;
  assert(setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &maxpkt, sizeof(int)) != -1);

  int ut = CTL_TIME_KEEPALIVE;
  assert(setsockopt(sock, IPPROTO_TCP, TCP_USER_TIMEOUT, &ut, sizeof(ut)) != -1);
}

int recv_cmd(int sock, CTL_COMMAND_t* cmd){
  return recv(sock, cmd, sizeof(CTL_COMMAND_t), 0);
}

int send_cmd(int sock, CTL_COMMAND_t* cmd){
  return send(sock, cmd, sizeof(CTL_COMMAND_t), 0);
}

int recv_task(int sock, flight_task_t* tsk){
  return recv(sock, tsk, sizeof(*tsk), 0);
}

int send_task(int sock, flight_task_t* tsk){
  return send(sock, tsk, sizeof(*tsk), 0);
}

static void on_accept_cb(struct ev_loop *loop, ev_io *w, int revents){
  printf("\tOn accept callback...\n");

  struct sockaddr_in client_info;
  int struct_len = sizeof(client_info);
  int client = accept(w->fd, (struct sockaddr *) &client_info, &struct_len);
  //enable_keepalive(w->fd);
  enable_keepalive(client);

  if(TIMEOUT_WTR != NULL){
    printf("Timer interruption\n");
    ev_timer_stop(EV_A_ TIMEOUT_WTR);
    free(TIMEOUT_WTR);
    TIMEOUT_WTR = NULL;
  }

  printf("\tClient connected: %s:%d\n", inet_ntoa(client_info.sin_addr), ntohs(client_info.sin_port));
  
  struct ev_io *client_wtr = malloc(sizeof(ev_io));
  ev_io_init(client_wtr, client_read_cb, client, EV_READ); 
  ev_io_start(loop, client_wtr);
}

static void client_read_cb(struct ev_loop *loop, ev_io *w, int revents){
  printf("\t\tClient callback...\n");

  flight_task_t tsk;
  CTL_COMMAND_t cmd;
  EVENT_t event = EVENT_TASK;
  ABORT_t abort = ABORT_TRUE;

  if(recv_cmd(w->fd, &cmd) <= 0) {
    perror("\t\tConnection refused");
    
    if(errno == ETIMEDOUT){
      printf("\tKeepalive Fly interrupt ->\n");
      bcshm_write(SHM_ABORT, &shm, &abort);
    }
    else{
      TIMEOUT_WTR = malloc(sizeof(ev_timer));
      ev_timer_init(TIMEOUT_WTR, timeout_cb, CTL_TIMER, 0.);
      ev_timer_start(loop, TIMEOUT_WTR);
    }

    ev_io_stop (EV_A_ w);
    close(w->fd);
    free(w);
    return;
  }

  printf("\t\tCMD: %u\n", cmd);
  ftc_tlm_t ftc_tlm;
  switch(cmd){
    case CTL_TASK:
      recv_task(w->fd, &tsk);

      bcshm_read(SHM_FTC_TLM, &shm, &ftc_tlm);

      if(ftc_tlm.state_flg == STATE_INIT){
        bcshm_write(SHM_FLIGHT_TASK, &shm, &tsk); 
        bcshm_write(SHM_EVENT, &shm, &event); 
        printf("\t\tTASK: %u, %d, %d\n", tsk.hight, tsk.start_time, tsk.hang_time);
      }  
      break;
    
    case CTL_INTERRUPT:
      bcshm_read(SHM_FTC_TLM, &shm, &ftc_tlm);
      if(ftc_tlm.state_flg != STATE_INIT){
        bcshm_write(SHM_ABORT, &shm, &abort);
      }
      break;
    
    default:
      break;  
  }
}

static void timeout_cb(struct ev_loop *loop, ev_timer *w, int revents){
  ABORT_t abort = ABORT_TRUE;
  printf("\tclient rst timeout Fly interrupt ->\n");
  bcshm_write(SHM_ABORT, &shm, &abort);
  ev_timer_stop(EV_A_ w);
  free(w);
  TIMEOUT_WTR = NULL;
  return;
}
